#!/usr/bin/env python3

# Copyright (c) 2005-2014 Fpemud <fpemud@sina.com>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.


import os
import re
import pathlib
from ...._plugin import KernelAddonPlugin


class Plugin(KernelAddonPlugin):

    def patch_kernel(self, context):
        # set swappiness to 1
        fullfn = os.path.join(context.get_kernel_work_dir(), "mm/vmscan.c")
        util.sed(fullfn, "int vm_swappiness = 60;", "int vm_swappiness = 1;", exceptionClass=self.PatchKernelError)

    def modify_kernel_config(self, context):
        ## basic ##############################################
        context.set_choice_by_path(["General setup", "Timers subsystem", "Timer tick handling"], "NO_HZ_IDLE")
        context.set_choice_by_path(["General setup", "Preemption Model"], "PREEMPT")

        # desktop related hardward support
        context.select_by_symbol("HIGH_RES_TIMERS", "y")

        ## tty ##############################################
        context.select_by_symbol("SERIAL_NONSTANDARD", "n")
        context.select_by_symbol("TTY_PRINTK", "n")
        context.select_by_symbol("IPMI_HANDLER", "n")                                                   # unfamiliar
        context.select_by_symbol("IPMB_DEVICE_INTERFACE", "n")                                          # unfamiliar
        context.select_by_symbol("TCG_TPM", "n")                                                        # unfamiliar
        context.select_by_symbol("TELCLOCK", "n")                                                       # unfamiliar
        context.select_by_symbol("XILLYBUS", "n")                                                       # unfamiliar
        context.select_by_symbol("XILLYUSB", "n")                                                       # unfamiliar
        context.select_by_symbol("NOZOMI", "n")                                                         # not support PCMCIA hardware
        context.select_by_symbol("MWAVE", "n")                                                          # unfamiliar

        # tty support
        context.select_by_symbol("TTY", "y")
        context.select_by_symbol("NULL_TTY", "y")                                                                       # use /dev/ttynull as /dev/console for init process
                                                                                                                        # ttynull is not appeared in /sys/class/tty/console/active, strange?!
        # serial device is under tty subsystem, not a good design
        context.select_by_symbol("SERIAL_8250_16550A_VARIANTS", "n")                                                    # enable it takes additional time at boot
        context.set_integer_by_symbol("SERIAL_8250_RUNTIME_UARTS", 0)                                                   # only support PNP serial device
        context.select_by_symbol("SERIAL_8250_EXTENDED", "n")
        context.select_tree_by_symbol("SERIAL_8250", "m,y", value_priority=context.ValuePriority.USER_POLICY)
        context.select_tree_by_symbol("SERIAL_8250_PCI", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        ## input ############################################
        context.select_by_symbol("INPUT", "y")
        context.select_by_symbol("INPUT_KEYBOARD", "y")
        context.select_by_symbol("KEYBOARD_ATKBD", "y")                                                                 # many notepads still use AT keyboard and, AT keyboard does not support PnP
        context.select_by_symbol("INPUT_MISC", "y")
        context.select_by_symbol("INPUT_UINPUT", "y")                                                                   # /dev/uinput is a very useful interface
        context.select_tree_by_path(["Device Drivers", "Input device support"], "m,y")
        context.select_tree_by_path(["Device Drivers", "HID support"], "m,y")

        ## sound ############################################
        context.select_by_symbol("SND_USB_6FIRE", "n")                                                                                                                                          # no firmware, we think it's rare
        context.select_by_symbol("SND_HDSP", "n")                                                                                                                                               # no firmware, we think it's rare
        context.select_by_symbol("SND_HDSPM", "n")                                                                                                                                              # no firmware, we think it's rare
        context.select_by_symbol("SND_CS46XX", "n")                                                                                                                                             # no firmware, we think it's rare
        context.select_by_symbol("SND_MIXART", "n")                                                                                                                                             # no firmware, we think it's rare
        context.select_by_symbol("SND_ASIHPI", "n")                                                                                                                                             # no firmware, we think it's rare
        context.select_by_symbol("SND_PCXHR", "n")                                                                                                                                              # no firmware, we think it's rare
        for sym_name, path_seq in context.list_symbols_in_tree_by_path(["Device Drivers", "Sound card support", "Advanced Linux Sound Architecture", "PCI sound devices"], with_path=True):
            if re.search("\\(Echoaudio\\) .*", path_seq[-1]) is not None:
                context.select_by_symbol(sym_name, "n")

        context.select_by_symbol("SOUND", "m")
        context.select_by_symbol("SND_SOC", "n")
        context.select_by_symbol("SND_PCSP", "n")
        context.select_by_symbol("SND_SEQ_DUMMY", "n")
        context.select_tree_by_symbol("SND_DRIVERS", "n")                                                               # disable all generic sound drivers, which are only used for testing purpose
        context.select_tree_by_symbol("SOUND", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        ## graphics #########################################
        context.select_tree_by_symbol("AGP", "m,y")
        context.select_tree_by_symbol("DRM", "m,y")
        for sym_name in context.list_symbols_in_tree_by_path(["Device Drivers", "Graphics support"]):
            if re.fullmatch("DRM_.*", sym_name) is not None:
                context.select_by_symbol(sym_name, "m,y", value_priority=context.ValuePriority.USER_POLICY)
        context.select_by_symbol("HSA_AMD", "y")
        context.select_tree_by_path(["Device Drivers", "Graphics support", "Backlight & LCD device support"], "m,y")

        ## usb ##############################################
        context.select_by_symbol("USBIP_CORE", "n")                                                                     # USB/IP seems to be an advanced feature
        context.select_by_symbol("MUSB_PIO_ONLY", "n")
        context.select_by_symbol("USB_XHCI_DBGCAP", "n")                                                                # seems debug?
        context.select_by_symbol("USB_HCD_TEST_MODE", "n")                                                              # seems experiment?
        context.select_by_symbol("USB_ISIGHTFW", "n")                                                                   # no firmware, we think it's rare
        context.select_by_symbol("USB_OTG_DISABLE_EXTERNAL_HUB", "n")
        context.select_tree_by_symbol("USB_SUPPORT", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        ## other ############################################                                # FIXME: belongs here?
        context.select_tree_by_path(["Device Drivers", "Character devices"], "m,y", value_priority=context.ValuePriority.USER_POLICY)

        # menu: LED Support
        context.select_tree_by_symbol("NEW_LEDS", "m,y")

        # menu: Real Time Clock
        context.select_by_symbol("RTC_HCTOSYS", "n")                                                                    # no service
        context.select_by_symbol("RTC_SYSTOHC", "n")                                                                    # no service
        context.select_tree_by_symbol("RTC_CLASS", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        # menu: DMA Engine support
        context.select_tree_by_symbol("DMADEVICES", "m,y")

        # menu: Multimedia support
        context.select_by_symbol("V4L_PLATFORM_DRIVERS", "n")                                                           # no rare
        context.select_tree_by_symbol("MEDIA_SUPPORT", "m,y", value_priority=context.ValuePriority.USER_POLICY)

        # menu: X86 Platform Specific Device Drivers
        context.select_by_symbol("X86_PLATFORM_DEVICES", "y")
        context.select_by_symbol("ACPI_WMI", "m,y")

        ## other ############################################           # FIXME: belongs here?
        for sym_name, path_seq in context.list_symbols_in_tree_by_path(["Cryptographic API"], with_path=True):
            if re.fullmatch("Userspace .*", path_seq[-1]) or re.fullmatch("User-space interface for .*", path_seq[-1]):
                context.select_by_symbol(sym_name, "m,y")
        context.select_tree_by_path(["Cryptographic API"], "n", value_priority=context.ValuePriority.USER_POLICY)            # remove all uneccessary options

        context.select_tree_by_path(["Library routines"], "n", value_priority=context.ValuePriority.USER_POLICY)             # remove all uneccessary options

        # strange, seems "[symbols:CGROUPS]=y" does not take full effect
        # context.select_by_symbol("CGROUP_HUGETLB", "y")
        # context.select_by_symbol("CGROUP_BPF", "y")

        # return [
        #     ## tty ##############################################
        #     "SERIAL_NONSTANDARD=n",
        #     "TTY_PRINTK=n",
        #     "IPMI_HANDLER=n",                                                      # unfamiliar
        #     "IPMB_DEVICE_INTERFACE=n",                                             # unfamiliar
        #     "TCG_TPM=n",                                                           # unfamiliar
        #     "TELCLOCK=n",                                                          # unfamiliar
        #     "XILLYBUS=n",                                                          # unfamiliar
        #     "XILLYUSB=n",                                                          # unfamiliar
        #     "NOZOMI=n",                                                            # not support PCMCIA hardware
        #     "MWAVE=n",                                                             # unfamiliar

        #     # tty support
        #     "TTY=y",
        #     "NULL_TTY=y",                                                          # use /dev/ttynull as /dev/console for init process
        #                                                                            # ttynull is not appeared in /sys/class/tty/console/active, strange?!

        #     # serial device is under tty subsystem, not a good design
        #     "SERIAL_8250_16550A_VARIANTS=n",                                       # enable it takes additional time at boot
        #     "SERIAL_8250_RUNTIME_UARTS=0",                                         # only support PNP serial device
        #     "SERIAL_8250_EXTENDED=n",
        #     "[symbols:SERIAL_8250]=m,y",
        #     "[symbols:SERIAL_8250_PCI]=m,y",
        #     #[symbols:/Device drivers/Character devices/Serial drivers]=n"             # pylkc can not find this menu?!

        #     ## input ############################################

        #     "INPUT=y",

        #     "INPUT_KEYBOARD=y",
        #     "KEYBOARD_ATKBD=y",                                        # many notepads still use AT keyboard and, AT keyboard does not support PnP
        #     "[symbols:INPUT_KEYBOARD]=n",                              # no deprecated

        #     "INPUT_MISC=y",
        #     "INPUT_UINPUT=y",                                          # /dev/uinput is a very useful interface
        #     "[symbols:INPUT_MISC]=n",

        #     "[symbols:/Device Drivers/Input device support]=m,y",
        #     "[symbols:/Device Drivers/HID support]=m,y",


        #     ## sound ############################################

        #     "SND_USB_6FIRE=n",                                                                                                                     # no firmware, we think it's rare
        #     "SND_HDSP=n",                                                                                                                          # no firmware, we think it's rare
        #     "SND_HDSPM=n",                                                                                                                         # no firmware, we think it's rare
        #     "SND_CS46XX=n",                                                                                                                        # no firmware, we think it's rare
        #     "SND_MIXART=n",                                                                                                                        # no firmware, we think it's rare
        #     "SND_ASIHPI=n",                                                                                                                        # no firmware, we think it's rare
        #     "SND_PCXHR=n",                                                                                                                         # no firmware, we think it's rare
        #     "[prompt-regex-symbols:\\(Echoaudio\\) .*:/Device Drivers/Sound card support/Advanced Linux Sound Architecture/PCI sound devices]=n",  # no firmware, we think it's rare

        #     #SND_LAYLA24=n                                               # no firmware, we think it's rare
        #     #SND_INDIGOIO
        #     #SND_INDIGO
        #     #SND_INDIGODJ
        #     #SND_ECHO3G
        #     #SND_GINA20
        #     #SND_MONA

        #     "SOUND=m",
        #     "SND_SOC=n",
        #     "SND_PCSP=n",
        #     "SND_SEQ_DUMMY=n",
        #     "[symbols:SND_DRIVERS]=n",    # disable all generic sound drivers, which are only used for testing purpose
        #     "[symbols:SOUND]=m,y",


        #     ## graphics #########################################

        #     "[symbols:AGP]=m,y",

        #     "DRM_NOUVEAU=n",              # it has compiling error

        #     "[symbols:DRM]=m,y",
        #     "DRM_LOAD_EDID_FIRMWARE=n",
        #     "[regex-symbols:DRM_.*:/Device Drivers/Graphics support]=m,y",
        #     "HSA_AMD=y",

        #     "[symbols:/Device Drivers/Graphics support/Backlight & LCD device support]=m,y",


        #     ## usb ##############################################

        #     "USBIP_CORE=n",                                             # USB/IP seems to be an advanced feature
        #     "MUSB_PIO_ONLY=n",

        #     "USB_XHCI_DBGCAP=n",                                        # seems debug?
        #     "USB_HCD_TEST_MODE=n",                                      # seems debug?
        #     "USB_DWC2_TRACK_MISSED_SOFS=n",                             # seems experiment?

        #     "USB_ISIGHTFW=n",                                           # no firmware, we think it's rare

        #     "USB_OTG_DISABLE_EXTERNAL_HUB=n",
        #     "[symbols:USB_SUPPORT]=m,y",


        #     ## other ############################################           # FIXME: belongs here?

        #     "[symbols:/Device Drivers/Character devices]=m,y",

        #     # menu: LED Support
        #     "[symbols:NEW_LEDS]=m,y",

        #     # menu: Real Time Clock
        #     "RTC_HCTOSYS=n",                                            # no service
        #     "RTC_SYSTOHC=n",                                            # no service
        #     "[symbols:RTC_CLASS]=m,y",

        #     # menu: DMA Engine support
        #     "[symbols:DMADEVICES]=m,y",

        #     # menu: Multimedia support
        #     "V4L_PLATFORM_DRIVERS=n",                                   # no rare
        #     "[symbols:MEDIA_SUPPORT]=m,y",

        #     # menu: X86 Platform Specific Device Drivers
        #     "X86_PLATFORM_DEVICES=y",
        #     "ACPI_WMI=m,y",


        #     ## other ############################################           # FIXME: belongs here?

        #     #	CONFIG_CRYPTO_USER_API_SKCIPHER			m
        #     #	CONFIG_CRYPTO_ECB				M
        #     #	CONFIG_CRYPTO_CBC				OK
        #     "CRYPTO_DES=m",                            # iwd need it
        #     "CRYPTO_DES3_EDE_X86_64=m",                # iwd need it

        #     "[prompt-regex-symbols:Userspace .*:/Cryptographic API]=m,y",
        #     "[prompt-regex-symbols:User-space interface for .*:/Cryptographic API]=m,y",
        #     "[symbols:/Cryptographic API]=n",                                                  # remove all uneccessary options

        #     "[symbols:/Library routines]=n",                                                   # remove all uneccessary options

        #     # strange, seems "[symbols:CGROUPS]=y" does not take full effect
        #     "CGROUP_HUGETLB=y",
        #     "CGROUP_BPF=y",
        # ]


class util:

    @staticmethod
    def sed(fn, pattern, repl, regex=False, exceptionClass=None):
        buf = pathlib.Path(fn).read_text()

        if not regex:
            if pattern not in buf and exceptionClass is not None:
                raise exceptionClass("Can not find the specified pattern")
            buf = buf.replace(pattern, repl)
        else:
            buf = re.sub(pattern, repl, buf)

        with open(fn, "w") as f:
            f.write(buf)
